/////////////////////////////////////////////////////////////////////////////
//
// (c) 2007 BinaryComponents Ltd.  All Rights Reserved.
//
// http://www.binarycomponents.com/
//
/////////////////////////////////////////////////////////////////////////////

using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using SuperListEx.Utility.Assemblies;
using SuperListEx.WinFormsUtility.Drawing;

namespace SuperListEx.Sections
{
  public class GroupSection : RowSection
  {
    #region vars
    private const int _separatorLineHeight = 2;
    private const int _margin = 7;
    private Rectangle _buttonRectangle;
    private int _groupIndentWidth;
    private static Resources _resources = new Resources();
    #endregion

    #region properties
    private Icon DrawIcon
    {
      get
      {
        Icon drawIcon = null;
        switch (GroupState)
        {
          case GroupState.Collapsed:
            drawIcon = _resources.ExpandIcon;
            break;
          case GroupState.Expanded:
            drawIcon = _resources.CollapseIcon;
            break;
        }
        return drawIcon;
      }
    }

    protected override int IndentWidth
    {
      get
      {
        return _groupIndentWidth;
      }
    }

    protected virtual string Text
    {
      get
      {
        int descendentCount = RowIdentifier.ItemCount;

        if (RowIdentifier.LastColumn.GroupItemAccessorWithParameter != null)
        {
          return string.Format("{0} ({1} {2})",
                               RowIdentifier.LastColumn.GroupItemAccessorWithParameter(Item, RowIdentifier.LastColumn.AccessorParameter),
                               descendentCount,
                               descendentCount == 1 ? ListControl.GroupSectionTextSingular : ListControl.GroupSectionTextPlural
                  );
        }

        else if (RowIdentifier.LastColumn.GroupItemAccessor != null)
        {
          return string.Format("{0} ({1} {2})",
                   RowIdentifier.LastColumn.GroupItemAccessor(Item),
                   descendentCount,
                   descendentCount == 1 ? ListControl.GroupSectionTextSingular : ListControl.GroupSectionTextPlural
                  );
        }

        else
        {
          return string.Format("{0} ({1} {2})",
                               RowIdentifier.LastColumn.ColumnItemAccessorWithParameter(Item, RowIdentifier.LastColumn.AccessorParameter),
                               descendentCount,
                               descendentCount == 1 ? ListControl.GroupSectionTextSingular : ListControl.GroupSectionTextPlural
                  );
        }
      }
    }

    protected virtual Font Font
    {
      get
      {
        return ListControl.GroupSectionFont == null ? Host.Font : ListControl.GroupSectionFont;
      }
    }


    public GroupState GroupState
    {
      get
      {
        return ListSection.GetGroupState(RowIdentifier);
      }
      set
      {
        ListSection.SetGroupState(RowIdentifier, value, true);
      }
    }
    #endregion

    #region constructors
    public GroupSection(ListControl listControl, RowIdentifier ri, HeaderSection headerSection, int position, int groupIndentWidth)
      : base(listControl, ri, headerSection, position)
    {
      Debug.Assert(ri is GroupIdentifier);
      _groupIndentWidth = groupIndentWidth;
    }
    #endregion

    #region public functions
    public override void Layout(GraphicsSettings gs, Size maximumSize)
    {
      SizeF size = gs.Graphics.MeasureString(Text, Font);
      if (size.Height < MinimumHeight)
      {
        size.Height = MinimumHeight;
      }
      Size = new Size(HeaderSection.Rectangle.Width, (int)size.Height + _margin + _separatorLineHeight);
    }

    public override void Paint(GraphicsSettings gs, Rectangle clipRect)
    {
      Rectangle rcText;
      GetDrawRectangles(out rcText, out _buttonRectangle);
      Rectangle rc = HostBasedRectangle;

      //
      // Fill indent area if any
      if (_groupIndentWidth > 0)
      {
        Rectangle rcIndent = new Rectangle(rc.X, rc.Y, _groupIndentWidth, rc.Height);

        PaintIndentArea(gs.Graphics, rcIndent);
      }

      gs.Graphics.DrawIcon(DrawIcon, _buttonRectangle.X, _buttonRectangle.Y);

      GdiPlusEx.DrawString(gs.Graphics, Text, Font, (Host.FocusedSection == ListSection && IsSelected) ? SystemColors.HighlightText : ListControl.GroupSectionForeColor, rcText, Alignment.Left, ListControl.GroupSectionVerticalAlignment, GdiPlusEx.TextSplitting.SingleLineEllipsis, GdiPlusEx.Ampersands.Display);

      Rectangle rcLine = rc;
      rcLine.X += _groupIndentWidth;
      PaintSeparatorLine(gs.Graphics, rcLine);
    }

    public void GetDrawRectangles(out Rectangle textRectangle, out Rectangle buttonRectangle)
    {
      int spacing = 4;
      Rectangle rc = HostBasedRectangle;

      rc.X += _groupIndentWidth + spacing;
      rc.Height -= _margin + _separatorLineHeight;
      rc.Y += _margin;
      buttonRectangle = new Rectangle(rc.X, rc.Y, DrawIcon.Width, DrawIcon.Height);
      rc.X += DrawIcon.Width + spacing;
      textRectangle = new Rectangle(rc.X, rc.Y - 1, rc.Width, rc.Height);
      switch (ListControl.GroupSectionVerticalAlignment)
      {
        case VerticalAlignment.Bottom:
          buttonRectangle.Y = textRectangle.Bottom - DrawIcon.Height;
          break;
        case VerticalAlignment.Center:
          buttonRectangle.Y = (textRectangle.Bottom - textRectangle.Height / 2) - DrawIcon.Height / 2;
          break;
      }
    }
    #endregion

    #region mouse events
    public override bool MouseDoubleClick(Point pt)
    {
      GroupState = GroupState == GroupState.Expanded ? GroupState.Collapsed : GroupState.Expanded;
      return true;
    }

    public override void MouseClick(MouseEventArgs e)
    {
      if (!_buttonRectangle.Contains(new Point(e.X, e.Y))) // stop selection if over + - button
      {
        base.MouseClick(e);
      }
    }

    public override void MouseDown(MouseEventArgs e)
    {
      if (_buttonRectangle.Contains(new Point(e.X, e.Y)) && e.Button == MouseButtons.Left)
      {
        GroupState = GroupState == GroupState.Expanded ? GroupState.Collapsed : GroupState.Expanded;
      }
      else
      {
        base.MouseDown(e);
      }
    }
    #endregion

    #region Resources

    /// <summary>
    /// Class that shares out the insertion window resources
    /// </summary>
    private class Resources : IDisposable
    {
      public Resources()
      {
        _referencesCount++;
      }

      public void Dispose()
      {
        if (!_disposed)
        {
          if (--_referencesCount == 0)
          {
            if (_collapseIcon != null)
            {
              _collapseIcon.Dispose();
              _collapseIcon = null;
            }
            if (_expandIcon != null)
            {
              _expandIcon.Dispose();
              _expandIcon = null;
            }
          }
          Debug.Assert(_referencesCount >= 0);
          _disposed = true;
        }
      }

      public Icon CollapseIcon
      {
        get
        {
          if (_collapseIcon == null)
          {
            _collapseIcon = _resources.GetIcon("CollapseGroup.ico");
          }
          return _collapseIcon;
        }
      }

      public Icon ExpandIcon
      {
        get
        {
          if (_expandIcon == null)
          {
            _expandIcon = _resources.GetIcon("ExpandGroup.ico");
          }
          return _expandIcon;
        }
      }

      private bool _disposed = false;
      private static int _referencesCount = 0;
      private static Icon _expandIcon = null;
      private static Icon _collapseIcon = null;
      private ManifestResources _resources = new ManifestResources("SuperListEx.Resources");
    }

    #endregion

    protected override void PaintSeparatorLine(Graphics g, Rectangle rc)
    {
      using (Pen pen = new Pen(SeperatorColor, _separatorLineHeight))
      {
        g.DrawLine(pen, new Point(rc.Left, rc.Bottom - _separatorLineHeight + 1), new Point(rc.Right, rc.Bottom - _separatorLineHeight + 1));
      }
    }
  }
}